<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>直播接收端（WebRTC）</title>
    <link rel="stylesheet" type="text/css" href="./iview.css">
    <style>
        * {margin: 0; padding: 0; box-sizing: border-box;}
        .row-flex {display: flex; align-items: center;}
        .row-flex-prefix {margin-right: .5em; cursor: pointer;}
        .row-flex-main {width: 0; flex-grow: 1;}
        .row-flex-suffix {margin-left: .5em;}
        html, body, #app {height: 100%;}
        #app {display: flex; flex-direction: column; padding: 15px;}
        .page-header {text-align: center;}
        .page-body {height: 0; flex-grow: 1; display: flex;}
        .page-body .ivu-col {padding: 10px;}
        .page-body fieldset {
            height: 100%; display: block; margin-inline-start: 2px; margin-inline-end: 2px; padding-block-start: 0.6em; padding-inline-start: 1em;
            padding-inline-end: 1em; padding-block-end: 0.85em; min-inline-size: min-content; border: 1px solid #bbb; border-radius: 6px;
        }
        .page-body fieldset legend {
            padding: 0 .5em; font-size: 1rem; color: #888; float: none; width: auto;
        }
        .page-body .fieldset-content {height: 100%;}
        .page-body .fieldset-content video {
          width: 100%; height: 100%; object-fit: contain; background-color: #333;
        }
        .row-flex {margin-bottom: 1em;}
    </style>
    <script type="text/javascript" src="./vue.min.js"></script>
    <script type="text/javascript" src="./iview.min.js"></script>
    <script type="text/javascript" src="./socket.io.min.js"></script>
</head>
<body>
<div id="app" v-cloak>
    <h2 class="page-header">直播接收端（WebRTC）</h2>
    <i-row class="page-body">
        <i-col :span="8">
            <fieldset>
                <legend>操作</legend>
                <div class="fieldset-content">
                    <div class="row-flex">
                        <div class="row-flex-prefix">服务端IP</div>
                        <div class="row-flex-main">
                            <i-input v-model.trim="serverIP" placeholder="请输入服务端IP" search enter-button="连接" @on-search="listenWebrtcServer" />
                        </div>
                    </div>
                    <div class="row-flex">
                        <i-button long @click="join" type="primary">加入直播间</i-button>
                    </div>
                    <div class="row-flex">
                        <i-button long @click="leave">退出直播间</i-button>
                    </div>
                </div>
            </fieldset>
        </i-col>
        <i-col :span="16">
            <fieldset>
                <legend>直播画面</legend>
                <div class="fieldset-content">
                    <video ref="video" autoplay="autoplay" controls></video>
                </div>
            </fieldset>
        </i-col>
    </i-row>
</div>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            serverIP: '',
            wsWaiter: null,
            peerConnection: null
        },
        methods: {
            // 连接webrtc websocket服务器
            listenWebrtcServer () {
                if (this.wsWaiter) { this.wsWaiter.close() }
                this.serverIP = this.serverIP || '127.0.0.1'
                this.wsWaiter = io.connect(`http://${this.serverIP}:7650/clients`)
                this.wsWaiter.on('resetConnection', (res) => {
                    console.log('resetConnection:', res)
                    this.leave()
                    this.join()
                })
                this.wsWaiter.on('canJoin', (res) => {
                    console.log('canJoin:', res)
                    this.createPeerConnection()
                })
                this.wsWaiter.on('remoteCandidate', (res) => {
                    console.log('remoteCandidate:', res)
                    let {data} = res || {}
                    if (!this.peerConnection || !data) return
                    this.peerConnection.addIceCandidate(data)
                })
                this.wsWaiter.on('remoteDescription', (res) => {
                    console.log('remoteDescription:', res)
                    let {data} = res || {}
                    if (!this.peerConnection || !data) return
                    this.peerConnection.setRemoteDescription(data)
                    this.createAnswer()
                })
                this.wsWaiter.on('closeConnection', (res) => {
                    console.log('closeConnection:', res)
                    if (!this.peerConnection) return
                    this.peerConnection.close()
                    this.peerConnection = null
                    this.loadStream(null)
                    this.$toast('直播间关闭了')
                })
            },
            createAnswer () {
                if (!this.peerConnection) return
                this.peerConnection.createAnswer().then((data) => {
                    this.peerConnection.setLocalDescription(data)
                    this.wsWaiter && this.wsWaiter.emit('description', data)
                }).catch((err) => {
                    console.error('createAnswer:', err)
                    this.$toast('createAnswer 时发生错误')
                })
            },
            createPeerConnection () {
                this.peerConnection = new RTCPeerConnection()
                this.peerConnection.onicecandidate = (e) => {
                    console.log('onicecandidate:', e)
                    if (!e.candidate) return
                    this.wsWaiter && this.wsWaiter.emit('candidate', e.candidate)
                }
                // 获取远程视频
                this.peerConnection.ontrack = (e) => {
                    this.loadStream(e.streams[0])
                }
            },
            // 加载视频流
            loadStream (stream) {
                if (!this.$refs.video) return
                try {
                    this.$refs.video.srcObject = stream
                } catch (err) {
                    this.$refs.video.src = window.URL.createObjectURL(stream)
                }
            },
            join () {
                this.wsWaiter && this.wsWaiter.emit('join')
            },
            leave () {
                if (this.peerConnection) {
                    this.peerConnection.close()
                    this.peerConnection = null
                    this.loadStream(null)
                }
                this.wsWaiter && this.wsWaiter.emit('leave')
            },
        }
    });
  </script>
</body>
</html>
